package com.engine.salary.report.entity.bo;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.cloudstore.eccom.pc.table.WeaTableColumn;
import com.engine.common.util.ServiceUtil;
import com.engine.salary.component.WeaTableColumnGroup;
import com.engine.salary.entity.hrm.dto.EmployeeInfoExpandDTO;
import com.engine.salary.entity.hrm.dto.FieldSetting;
import com.engine.salary.entity.salaryacct.po.SalaryAcctEmployeePO;
import com.engine.salary.report.common.constant.SalaryStatisticsDimensionConstant;
import com.engine.salary.report.entity.dto.SalaryStatisticsItemRuleDTO;
import com.engine.salary.report.entity.param.ExportColumn;
import com.engine.salary.report.entity.param.ExportCommonParam;
import com.engine.salary.report.entity.param.SalaryStatisticsDimensionSaveParam;
import com.engine.salary.report.entity.param.SalaryStatisticsReportDataQueryParam;
import com.engine.salary.report.entity.po.SalaryStatisticsDimensionPO;
import com.engine.salary.report.entity.po.SalaryStatisticsItemPO;
import com.engine.salary.report.entity.po.SalaryStatisticsReportPO;
import com.engine.salary.report.enums.SalaryStatisticsItemStringRuleEnum;
import com.engine.salary.report.enums.UnitTypeEnum;
import com.engine.salary.report.service.SalaryStatisticsDimensionService;
import com.engine.salary.report.service.impl.SalaryStatisticsDimensionServiceImpl;
import com.engine.salary.report.util.ReportDataUtil;
import com.engine.salary.report.util.ReportTimeUtil;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.page.PageInfo;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import weaver.hrm.User;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 薪酬统计报表
 */
public class SalaryStatisticsReportBO {

    public static final String COUNT = "count";
    public static final String SUM = "sum";
    public static final String AVG = "avg";
    public static final String MAX = "max";
    public static final String MIN = "min";
    public static final String MEDIAN = "median";

    public static final String ZERO = "0";
    public static final String ONE = "1";
    public static final String COMMA = ",";
    public static final String UD = "_";

    public static final String K_NOW = "_now";
    public static final String K_RATIO = "_ratio";
    public static final String K_LAST = "_last";
    public static final String K_SAME = "_same";

    public static final String P_NOW = "_now";
    public static final String P_LAST = "_last";
    public static final String P_SAME = "_same";
    public static final String P_RATIO = "_ratio";
    public static final String P_M2M_D_VALUE = "_m2m_d_value";
    public static final String P_M2M_INCREASE = "_m2m_increase";
    public static final String P_M2M_UPPER_LIMIT = "_m2m_upperLimit";
    public static final String P_M2M_LOWER_LIMIT = "_m2m_lowerLimit";
    public static final String P_Y2Y_D_VALUE = "_y2y_d_value";
    public static final String P_Y2Y_INCREASE = "_y2y_increase";
    public static final String P_Y2Y_UPPER_LIMIT = "_y2y_upperLimit";
    public static final String P_Y2Y_LOWER_LIMIT = "_y2y_lowerLimit";

    public static final String COL_WIDTH = "150";

    public static final String PERCENT = "%";
    public static final String D_ZERO = "0.00";

    public static final BigDecimal HUNDRED = new BigDecimal(100);

    public static final String G_NO_ITEM = "noItem";
    public static final String G_YEAR = "year";
    public static final String G_ITEM = "ITEM";

    private static SalaryStatisticsDimensionService getSalaryStatisticsDimensionService() {
        return ServiceUtil.getService(SalaryStatisticsDimensionServiceImpl.class, new User(1));
    }

    /**
     * 参数转换
     *
     * @param param
     * @param po
     */
    public static void poToQueryParam(SalaryStatisticsReportDataQueryParam param, SalaryStatisticsReportPO po) {
        String salaryStartMonth = param.getSalaryStartMonth();
        if (salaryStartMonth == null) {
            param.setSalaryStartMonth(SalaryDateUtil.getFormatYearMonth(po.getSalaryStartMonth()));
        } else {
            param.setSalaryStartMonth(SalaryDateUtil.getFormatYearMonth(SalaryDateUtil.dateStrToLocalDate(salaryStartMonth)));
        }
        String salaryEndMonth = param.getSalaryEndMonth();
        if (salaryEndMonth == null) {
            param.setSalaryEndMonth(SalaryDateUtil.getFormatYearMonth(po.getSalaryEndMonth()));
        } else {
            param.setSalaryEndMonth(SalaryDateUtil.getFormatYearMonth(SalaryDateUtil.dateStrToLocalDate(salaryEndMonth)));
        }

        String key = "id";
        if (po.getTaxAgentSetting() != null) {
            param.setTaxAgent(((List<Map>) JSON.parseArray(po.getTaxAgentSetting(), Map.class)).stream().map(m -> Long.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        }
        if (po.getSalarySobSetting() != null) {
            param.setSalarySob(((List<Map>) JSON.parseArray(po.getSalarySobSetting(), Map.class)).stream().map(m -> Long.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        }
//        param.setIncomeCategory(((List<Map>) JSON.parseArray(po.getIncomeCategorySetting(), Map.class)).stream().map(m -> Integer.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        if (po.getSubCompanySetting() != null) {
            param.setSubCompany(((List<Map>) JSON.parseArray(po.getSubCompanySetting(), Map.class)).stream().map(m -> Long.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        }
        if (po.getDepartSetting() != null) {
            param.setDepart(((List<Map>) JSON.parseArray(po.getDepartSetting(), Map.class)).stream().map(m -> Long.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        }
//        param.setGrade(((List<Map>) JSON.parseArray(po.getGradeSetting(), Map.class)).stream().map(m -> Long.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        if (po.getPositionSetting() != null) {
            param.setPosition(((List<Map>) JSON.parseArray(po.getPositionSetting(), Map.class)).stream().map(m -> Long.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        }
        if (po.getStatusSetting() != null) {
            param.setStatus(((List<Map>) JSON.parseArray(po.getStatusSetting(), Map.class)).stream().map(m -> m.get(key).toString()).collect(Collectors.toList()));
        }
        if (po.getEmployeeSetting() != null) {
            param.setEmployee(((List<Map>) JSON.parseArray(po.getEmployeeSetting(), Map.class)).stream().map(m -> Long.valueOf(m.get(key).toString())).collect(Collectors.toList()));
        }
        if (po.getHiredateSetting() != null) {
            param.setHiredate(JSON.parseArray(po.getHiredateSetting(), Date.class));
        }
//        param.setLeavedate(JSON.parseArray(po.getLeavedateSetting(), LocalDate.class));
    }

    /**
     * 参数转换获取上一期
     *
     * @param param
     * @param dimension
     * @return
     */
    public static SalaryStatisticsReportDataQueryParam lastParamConvert(SalaryStatisticsReportDataQueryParam param, SalaryStatisticsDimensionPO dimension) {
        SalaryStatisticsReportDataQueryParam lastParam = new SalaryStatisticsReportDataQueryParam();
        BeanUtils.copyProperties(param, lastParam);
        // 薪资所属月
        if (SalaryStatisticsDimensionConstant.DM_SALARY_MONTH.equals(dimension.getDimCode())) {
            lastParam.setSalaryStartMonth(ReportTimeUtil.getLastYearMonth(param.getSalaryStartMonth()));
            lastParam.setSalaryEndMonth(ReportTimeUtil.getLastYearMonth(param.getSalaryEndMonth()));
            // 年份
        } else if (SalaryStatisticsDimensionConstant.DM_YEAR.equals(dimension.getDimCode())) {
            lastParam.setSalaryStartMonth(ReportTimeUtil.getSameYearMonth(param.getSalaryStartMonth()));
            lastParam.setSalaryEndMonth(ReportTimeUtil.getSameYearMonth(param.getSalaryEndMonth()));
            // 半年度
        } else if (SalaryStatisticsDimensionConstant.DM_HALF_YEAR.equals(dimension.getDimCode())) {
            lastParam.setSalaryStartMonth(ReportTimeUtil.getPlusYearMonth(param.getSalaryStartMonth(), -6));
            lastParam.setSalaryEndMonth(ReportTimeUtil.getPlusYearMonth(param.getSalaryEndMonth(), -6));
            // 季度
        } else if (SalaryStatisticsDimensionConstant.DM_QUARTER.equals(dimension.getDimCode())) {
            lastParam.setSalaryStartMonth(ReportTimeUtil.getPlusYearMonth(param.getSalaryStartMonth(), -3));
            lastParam.setSalaryEndMonth(ReportTimeUtil.getPlusYearMonth(param.getSalaryEndMonth(), -3));
        } else {
            Map<String, String> timeDiff = ReportTimeUtil.getLastRange(param.getSalaryStartMonth(), param.getSalaryEndMonth());
            lastParam.setSalaryStartMonth(timeDiff.get("startMonth"));
            lastParam.setSalaryEndMonth(timeDiff.get("endMonth"));
        }
        return lastParam;
    }

    public static SalaryStatisticsReportDataQueryParam sameParamConvert(SalaryStatisticsReportDataQueryParam param) {
        SalaryStatisticsReportDataQueryParam sameParam = new SalaryStatisticsReportDataQueryParam();
        BeanUtils.copyProperties(param, sameParam);
        sameParam.setSalaryStartMonth(ReportTimeUtil.getSameYearMonth(param.getSalaryStartMonth()));
        sameParam.setSalaryEndMonth(ReportTimeUtil.getSameYearMonth(param.getSalaryEndMonth()));
        return sameParam;
    }

    /**
     * 计算项目
     *
     * @param list
     * @param lastList
     * @param salaryAcctResultValueMap
     * @param salaryStatisticsItemList
     * @return
     */
    public static Map<String, String> calculateItem(List<SalaryAcctEmployeePO> list, List<SalaryAcctEmployeePO> lastList, List<SalaryAcctEmployeePO> sameList, Map<Long, Map<String, String>> salaryAcctResultValueMap, List<SalaryStatisticsItemPO> salaryStatisticsItemList) {
        List<Map<String, String>> nowDetail = po2map(list, salaryAcctResultValueMap);
        List<Map<String, String>> lastDetail = po2map(lastList, salaryAcctResultValueMap);
        List<Map<String, String>> sameDetail = po2map(sameList, salaryAcctResultValueMap);
        Map<String, String> result = new HashMap<>();
        salaryStatisticsItemList.forEach(item -> {
            // 1.计数规则数据处理
            calculate4Count(result, item, nowDetail, lastDetail, sameDetail);
            // 2.求和规则数据处理
            calculate4Sum(result, item, nowDetail, lastDetail, sameDetail);
            // 3.平均值规则数据处理
            calculate4Avg(result, item, nowDetail, lastDetail, sameDetail);
            // 4.最大值规则数据处理
            calculate4Max(result, item, nowDetail, lastDetail, sameDetail);
            // 5.最小值规则数据处理
            calculate4Min(result, item, nowDetail, lastDetail, sameDetail);
            // 6.中位数规则数据处理
            calculate4Median(result, item, nowDetail, lastDetail, sameDetail);
            // 7.最近值
            calculate4Last(result, item, nowDetail, lastDetail, sameDetail);
            // 8.最久值
            calculate4Old(result, item, nowDetail, lastDetail, sameDetail);
            // 9.最频繁
            calculate4Frequent(result, item, nowDetail, lastDetail, sameDetail);
            // 10.平铺
            calculate4Tile(result, item, nowDetail, lastDetail, sameDetail);
        });
        return result;
    }

    private static void calculate4Count(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO countRule = JSON.parseObject(item.getCountRule(), SalaryStatisticsItemRuleDTO.class);
        if (countRule == null || countRule.getTotalValue() != 1) {
            return;
        }
        BigDecimal countDecimal = new BigDecimal(CollectionUtils.isNotEmpty(nowDetail) ? nowDetail.size() : 0);
        // 本期
        if (countRule.getTotalValue() == 1) {
            // 本期和占比赋值
            nowAndRatio(result, item, countRule, COUNT, countDecimal);
        }
        // 上期
        if (countRule.getM2mValue() == 1) {
            BigDecimal lastCountDecimal = new BigDecimal(CollectionUtils.isNotEmpty(lastDetail) ? lastDetail.size() : 0);
            // 上期和环比赋值
            lastM2m(result, item, countRule, COUNT, countDecimal, lastCountDecimal);
        }
        // 同期
        if (countRule.getY2yValue() == 1) {
            BigDecimal sameCountDecimal = new BigDecimal(CollectionUtils.isNotEmpty(sameDetail) ? sameDetail.size() : 0);
            // 同期和同比赋值
            sameY2y(result, item, countRule, COUNT, countDecimal, sameCountDecimal);
        }
    }


    private static void calculate4Sum(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO sumRule = JSON.parseObject(item.getSumRule(), SalaryStatisticsItemRuleDTO.class);
        if (sumRule == null || sumRule.getTotalValue() != 1) {
            return;
        }
        BigDecimal sumDecimal = new BigDecimal(ZERO);
        if (sumRule.getTotalValue() == 1) {
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId))  && NumberUtil.isNumber(x.get(itemId).trim())) {
                            sumDecimal = sumDecimal.add(new BigDecimal(x.get(itemId).trim()));
                        }
                    }
                }
            }
            nowAndRatio(result, item, sumRule, SUM, sumDecimal);
        }
        // 上期
        if (sumRule.getM2mValue() == 1) {
            BigDecimal lastSumDecimal = new BigDecimal(ZERO);
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : lastDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            lastSumDecimal = lastSumDecimal.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            lastM2m(result, item, sumRule, SUM, sumDecimal, lastSumDecimal);
        }
        // 同期
        if (sumRule.getY2yValue() == 1) {
            BigDecimal sameSumDecimal = new BigDecimal(ZERO);
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : sameDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            sameSumDecimal = sameSumDecimal.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            sameY2y(result, item, sumRule, SUM, sumDecimal, sameSumDecimal);
        }
    }

    private static void calculate4Avg(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO avgRule = JSON.parseObject(item.getAvgRule(), SalaryStatisticsItemRuleDTO.class);
        if (avgRule == null || avgRule.getTotalValue() != 1) {
            return;
        }
        BigDecimal avgDecimal = new BigDecimal(ZERO);
        // 本期
        if (avgRule.getTotalValue() == 1) {
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId)) && NumberUtil.isNumber(x.get(itemId).trim())) {
                            avgDecimal = avgDecimal.add(new BigDecimal(x.get(itemId).trim()));
                        }
                    }
                }
            }
            avgDecimal = avgDecimal.divide(new BigDecimal(CollectionUtils.isEmpty(nowDetail) ? 1 : nowDetail.size()), 2, RoundingMode.HALF_UP);
            nowAndRatio(result, item, avgRule, AVG, avgDecimal);
        }
        // 上期
        if (avgRule.getM2mValue() == 1) {
            BigDecimal lastAvgDecimal = new BigDecimal(ZERO);
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : lastDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            lastAvgDecimal = lastAvgDecimal.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            lastAvgDecimal = lastAvgDecimal.divide(new BigDecimal(CollectionUtils.isEmpty(lastDetail) ? 1 : lastDetail.size()), 2, RoundingMode.HALF_UP);
            lastM2m(result, item, avgRule, AVG, avgDecimal, lastAvgDecimal);
        }
        // 同期
        if (avgRule.getY2yValue() == 1) {
            BigDecimal sameAvgDecimal = new BigDecimal(ZERO);
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : sameDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            sameAvgDecimal = sameAvgDecimal.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            sameAvgDecimal = sameAvgDecimal.divide(new BigDecimal(CollectionUtils.isEmpty(sameDetail) ? 1 : sameDetail.size()), 2, RoundingMode.HALF_UP);
            sameY2y(result, item, avgRule, AVG, avgDecimal, sameAvgDecimal);
        }
    }

    private static void calculate4Max(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO maxRule = JSON.parseObject(item.getMaxRule(), SalaryStatisticsItemRuleDTO.class);
        if (maxRule == null || maxRule.getTotalValue() != 1) {
            return;
        }
        BigDecimal maxDecimal = new BigDecimal(ZERO);
        // 本期
        if (maxRule.getTotalValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId)) && NumberUtil.isNumber(x.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(x.get(itemId).trim()));
                        }
                    }
                }
            }
            // 本期合计
            maxDecimal = max(decimalList);
            nowAndRatio(result, item, maxRule, MAX, maxDecimal);
        }
        // 上期
        if (maxRule.getM2mValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : lastDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            BigDecimal lastMaxDecimal = max(decimalList);
            lastM2m(result, item, maxRule, MAX, maxDecimal, lastMaxDecimal);
        }
        // 同期
        if (maxRule.getY2yValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : sameDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            BigDecimal sameMaxDecimal = max(decimalList);
            sameY2y(result, item, maxRule, MAX, maxDecimal, sameMaxDecimal);
        }
    }

    private static void calculate4Min(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO minRule = JSON.parseObject(item.getMinRule(), SalaryStatisticsItemRuleDTO.class);
        if (minRule == null || minRule.getTotalValue() != 1) {
            return;
        }
        BigDecimal minDecimal = new BigDecimal(ZERO);
        // 本期
        if (minRule.getTotalValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId)) && NumberUtil.isNumber(x.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(x.get(itemId).trim()));
                        }
                    }
                }
            }
            // 本期合计
            minDecimal = min(decimalList);
            nowAndRatio(result, item, minRule, MIN, minDecimal);
        }
        // 上期
        if (minRule.getM2mValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : lastDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim()) ) {
                            decimalList.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            BigDecimal lastMinDecimal = min(decimalList);
            lastM2m(result, item, minRule, MIN, minDecimal, lastMinDecimal);
        }
        // 同期
        if (minRule.getY2yValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : sameDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            BigDecimal sameMinDecimal = min(decimalList);
            sameY2y(result, item, minRule, MIN, minDecimal, sameMinDecimal);
        }
    }

    private static void calculate4Median(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO medianRule = JSON.parseObject(item.getMedianRule(), SalaryStatisticsItemRuleDTO.class);
        if (medianRule == null || medianRule.getTotalValue() != 1) {
            return;
        }
        BigDecimal medianDecimal = new BigDecimal(ZERO);
        // 本期
        if (medianRule.getTotalValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId)) && NumberUtil.isNumber(x.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(x.get(itemId).trim()));
                        }
                    }
                }
            }
            // 本期合计
            medianDecimal = median(decimalList);
            nowAndRatio(result, item, medianRule, MEDIAN, medianDecimal);
        }
        // 上期
        if (medianRule.getM2mValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : lastDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            BigDecimal lastMedianDecimal = median(decimalList);
            lastM2m(result, item, medianRule, MEDIAN, medianDecimal, lastMedianDecimal);
        }
        // 同期
        if (medianRule.getY2yValue() == 1) {
            List<BigDecimal> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] lastItemValues = item.getItemValue().split(COMMA);
                for (String itemId : lastItemValues) {
                    for (Map<String, String> y : sameDetail) {
                        if (MapUtils.isNotEmpty(y) && StringUtils.isNotBlank(y.get(itemId)) && NumberUtil.isNumber(y.get(itemId).trim())) {
                            decimalList.add(new BigDecimal(y.get(itemId).trim()));
                        }
                    }
                }
            }
            BigDecimal sameMedianDecimal = median(decimalList);
            sameY2y(result, item, medianRule, MEDIAN, medianDecimal, sameMedianDecimal);
        }
    }

    /**
     * 最新的值
     *
     * @param result
     * @param item
     * @param nowDetail
     * @param lastDetail
     * @param sameDetail
     */
    private static void calculate4Last(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO lastRule = JSON.parseObject(item.getLastRule(), SalaryStatisticsItemRuleDTO.class);
        if (lastRule == null || lastRule.getTotalValue() != 1) {
            return;
        }
        String lastValue = "";
        // 本期
        if (lastRule.getTotalValue() == 1) {
            List<String> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId))) {
                            decimalList.add(x.get(itemId));
                        }
                    }
                }
            }
            // 本期最近值
            lastValue = last(decimalList);
            nowAndRatio(result, item, lastRule, SalaryStatisticsItemStringRuleEnum.LAST.getValue(), lastValue);
        }
    }

    /**
     * 最旧的值
     *
     * @param result
     * @param item
     * @param nowDetail
     * @param lastDetail
     * @param sameDetail
     */
    private static void calculate4Old(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO oldRule = JSON.parseObject(item.getOldRule(), SalaryStatisticsItemRuleDTO.class);
        if (oldRule == null || oldRule.getTotalValue() != 1) {
            return;
        }
        String value = "";
        // 本期
        if (oldRule.getTotalValue() == 1) {
            List<String> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId))) {
                            decimalList.add(x.get(itemId));
                        }
                    }
                }
            }
            // 本期最近值
            value = old(decimalList);
            nowAndRatio(result, item, oldRule, SalaryStatisticsItemStringRuleEnum.OLD.getValue(), value);
        }
    }

    /**
     * 最频繁
     *
     * @param result
     * @param item
     * @param nowDetail
     * @param lastDetail
     * @param sameDetail
     */
    private static void calculate4Frequent(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO frequentRule = JSON.parseObject(item.getFrequentRule(), SalaryStatisticsItemRuleDTO.class);
        if (frequentRule == null || frequentRule.getTotalValue() != 1) {
            return;
        }
        String value = "";
        // 本期
        if (frequentRule.getTotalValue() == 1) {
            List<String> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId))) {
                            decimalList.add(x.get(itemId));
                        }
                    }
                }
            }
            // 本期最近值
            value = frequent(decimalList);
            nowAndRatio(result, item, frequentRule, SalaryStatisticsItemStringRuleEnum.FREQUENT.getValue(), value);
        }
    }

    /**
     * 平铺
     *
     * @param result
     * @param item
     * @param nowDetail
     * @param lastDetail
     * @param sameDetail
     */
    private static void calculate4Tile(Map<String, String> result, SalaryStatisticsItemPO item, List<Map<String, String>> nowDetail, List<Map<String, String>> lastDetail, List<Map<String, String>> sameDetail) {
        SalaryStatisticsItemRuleDTO tileRule = JSON.parseObject(item.getTileRule(), SalaryStatisticsItemRuleDTO.class);
        if (tileRule == null || tileRule.getTotalValue() != 1) {
            return;
        }
        String value = "";
        // 本期
        if (tileRule.getTotalValue() == 1) {
            List<String> decimalList = Lists.newArrayList();
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    for (Map<String, String> x : nowDetail) {
                        if (MapUtils.isNotEmpty(x) && StringUtils.isNotBlank(x.get(itemId))) {
                            decimalList.add(x.get(itemId));
                        }
                    }
                }
            }
            // 本期最近值
            value = tile(decimalList);
            nowAndRatio(result, item, tileRule, SalaryStatisticsItemStringRuleEnum.TILE.getValue(), value);
        }
    }


    /**
     * 本期和占比赋值
     *
     * @param result
     * @param item
     * @param rule
     * @param ruleKey
     * @param sumDecimal
     */
    private static void nowAndRatio(Map<String, String> result, SalaryStatisticsItemPO item, SalaryStatisticsItemRuleDTO rule, String ruleKey, BigDecimal sumDecimal) {
        String keyPrefix = item.getId().toString() + UD + ruleKey;
        // 本期合计
        result.put(keyPrefix + K_NOW, ReportDataUtil.thousandthConvert(unitTypeConvert(sumDecimal, item.getUnitType())));
        // 占比：和本期合计一样的值，便于计算
        if (rule.getRatioValue() == 1) {
            result.put(keyPrefix + K_RATIO, result.get(keyPrefix + K_NOW));
        }
    }

    /**
     * 本期和占比赋值
     *
     * @param result
     * @param item
     * @param rule
     * @param ruleKey
     * @param sumDecimal
     */
    private static void nowAndRatio(Map<String, String> result, SalaryStatisticsItemPO item, SalaryStatisticsItemRuleDTO rule, String ruleKey, String sumDecimal) {
        String keyPrefix = item.getId().toString() + UD + ruleKey;
        // 本期合计
        result.put(keyPrefix + K_NOW, sumDecimal);
    }

    /**
     * 环比赋值
     *
     * @param result
     * @param item
     * @param rule
     * @param ruleKey
     * @param nowValue
     * @param lastValue
     */
    private static void lastM2m(Map<String, String> result, SalaryStatisticsItemPO item, SalaryStatisticsItemRuleDTO rule, String ruleKey, BigDecimal nowValue, BigDecimal lastValue) {
        String keyPrefix = item.getId().toString() + UD + ruleKey;
        // 上期合计
        result.put(keyPrefix + P_LAST, ReportDataUtil.thousandthConvert(unitTypeConvert(lastValue, item.getUnitType())));
        // 合计环比差值
        result.put(keyPrefix + P_M2M_D_VALUE, ReportDataUtil.thousandthConvert(unitTypeConvert(nowValue.subtract(lastValue), item.getUnitType())));
        // 合计环比增幅
        result.put(keyPrefix + P_M2M_INCREASE, radio(nowValue, lastValue));
        // 合计环比上限
        result.put(keyPrefix + P_M2M_UPPER_LIMIT, rule.getM2mUpperLimit().toString() + PERCENT);
        // 合计环比下限
        result.put(keyPrefix + P_M2M_LOWER_LIMIT, rule.getM2mLowerLimit().toString() + PERCENT);
    }

    /**
     * 同比赋值
     *
     * @param result
     * @param item
     * @param rule
     * @param ruleKey
     * @param nowValue
     * @param sameValue
     */
    private static void sameY2y(Map<String, String> result, SalaryStatisticsItemPO item, SalaryStatisticsItemRuleDTO rule, String ruleKey, BigDecimal nowValue, BigDecimal sameValue) {
        String keyPrefix = item.getId().toString() + UD + ruleKey;
        // 同期合计
        result.put(keyPrefix + P_SAME, ReportDataUtil.thousandthConvert(unitTypeConvert(sameValue, item.getUnitType())));
        // 合计同比差值
        result.put(keyPrefix + P_Y2Y_D_VALUE, ReportDataUtil.thousandthConvert(unitTypeConvert(nowValue.subtract(sameValue), item.getUnitType())));
        // 合计同比增幅
        result.put(keyPrefix + P_Y2Y_INCREASE, radio(nowValue, sameValue));
        // 合计同比上限
        result.put(keyPrefix + P_Y2Y_UPPER_LIMIT, rule.getY2yUpperLimit() == null ? "" : rule.getY2yUpperLimit().toString() + PERCENT);
        // 合计同比下限
        result.put(keyPrefix + P_Y2Y_LOWER_LIMIT, rule.getY2yLowerLimit() == null ? "" : rule.getY2yLowerLimit().toString() + PERCENT);
    }

    /**
     * 最大值
     *
     * @param list
     * @return
     */
    public static BigDecimal max(List<BigDecimal> list) {
        if (CollectionUtils.isEmpty(list)) return new BigDecimal(D_ZERO);
//        BigDecimal max = Collections.max(list);

        BigDecimal sum = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            sum = sum.max(list.get(i));
        }
        return sum;
    }

    /**
     * 最小值
     *
     * @param list
     * @return
     */
    public static BigDecimal min(List<BigDecimal> list) {
        if (CollectionUtils.isEmpty(list)) return new BigDecimal(D_ZERO);
//        BigDecimal min = Collections.min(list);
        BigDecimal sum = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            sum = sum.min(list.get(i));
        }
        return sum;
    }

    /**
     * 中位数
     *
     * @param list
     * @return
     */
    public static BigDecimal median(List<BigDecimal> list) {
        if (CollectionUtils.isEmpty(list)) return new BigDecimal(D_ZERO);
        Collections.sort(list);
        int size = list.size();
        if (size % 2 == 1) {
            return list.get((size - 1) / 2);
        } else {
            return (list.get(size / 2 - 1).add(list.get(size / 2))).divide(new BigDecimal(2), 2, RoundingMode.HALF_UP);
        }
    }

    /**
     * 最新的值
     *
     * @param list
     * @return
     */
    public static String last(List<String> list) {
        if (CollectionUtils.isEmpty(list)) {
            return "";
        }
        return list.get(0);
    }

    /**
     * 最旧的值
     *
     * @param list
     * @return
     */
    public static String old(List<String> list) {
        if (CollectionUtils.isEmpty(list)) {
            return "";
        }
        return list.get(list.size() - 1);
    }

    /**
     * 最频繁
     *
     * @param list
     * @return
     */
    public static String frequent(List<String> list) {
        if (CollectionUtils.isEmpty(list)) {
            return "";
        }
        return list.stream()
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                .entrySet()
                .stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .orElse("");
    }

    /**
     * 平铺
     *
     * @param list
     * @return
     */
    public static String tile(List<String> list) {
        if (CollectionUtils.isEmpty(list)) {
            return "";
        }
        return Joiner.on(",").join((Iterable<?>) list);
    }


    public static List<Map<String, String>> po2map(List<SalaryAcctEmployeePO> list, Map<Long, Map<String, String>> salaryAcctResultValueMap) {
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        return list.stream().map(sa -> salaryAcctResultValueMap.get(sa.getId())).collect(Collectors.toList());
    }

    public static String unitTypeConvert(BigDecimal bigDecimal, Integer unitType) {
        switch (unitType) {
            case 3:
                return bigDecimal.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP).toPlainString();
            case 4:
                return bigDecimal.divide(BigDecimal.valueOf(1000000), 2, RoundingMode.HALF_UP).toPlainString();
            default:
                return bigDecimal.toPlainString();
        }
    }

    public static String radio(BigDecimal origin, BigDecimal last) {
        if (ReportDataUtil.zeroJudge(last)) {
            return "--";
        }
        BigDecimal subtract = origin.subtract(last).multiply(HUNDRED);
        BigDecimal divide = subtract.divide(last, 2, RoundingMode.HALF_UP);
        return divide.toPlainString() + PERCENT;
    }

    /**
     * 定量-组距式分组list分割
     * 说明：
     * 1.如果是年：每个人的年份值在这个区间内；如果没有所属字段：各个字段值在区间内；如果有所属字敦：则该所属字段在区间内的数据
     * 2.区间内的话，就把id搞出来
     * 3.符合id的list筛选出来返回
     *
     * @param groupSpacing
     * @param groupBelong
     * @param dimCode
     * @param list
     * @param yearMap
     * @param salaryAcctResultValueMap
     * @param salaryStatisticsItemList
     * @return
     */
    public static List<SalaryAcctEmployeePO> listAcctEmpByRationGroupSpacing(SalaryStatisticsDimensionSaveParam.Setting4RationGroupSpacing groupSpacing, String groupBelong, String dimCode, List<SalaryAcctEmployeePO> list, Map<Long, String> yearMap, Map<Long, Map<String, String>> salaryAcctResultValueMap, List<SalaryStatisticsItemPO> salaryStatisticsItemList) {
        Set<Long> ids = Sets.newHashSet();

        for (SalaryAcctEmployeePO sa : list) {
            salaryStatisticsItemList.forEach(item -> {
                BigDecimal sumDecimal = handle4GroupValue(groupBelong, dimCode, sa, yearMap, salaryAcctResultValueMap, item);
                boolean isRight = ((!groupSpacing.isIncludeStart() && sumDecimal.compareTo(new BigDecimal(groupSpacing.getStartValue())) > 0)
                        || (groupSpacing.isIncludeStart() && sumDecimal.compareTo(new BigDecimal(groupSpacing.getStartValue())) >= 0))
                        && ((!groupSpacing.isIncludeEnd() && sumDecimal.compareTo(new BigDecimal(groupSpacing.getEndValue())) < 0)
                        || (groupSpacing.isIncludeEnd() && sumDecimal.compareTo(new BigDecimal(groupSpacing.getEndValue())) <= 0));
                if (isRight) {
                    ids.add(sa.getId());
                }
            });
        }

        return list.stream().filter(i -> ids.contains(i.getId())).collect(Collectors.toList());
    }

    private static BigDecimal handle4GroupValue(String groupBelong, String dimCode, SalaryAcctEmployeePO sa, Map<Long, String> yearMap, Map<Long, Map<String, String>> salaryAcctResultValueMap, SalaryStatisticsItemPO item) {
        BigDecimal sumDecimal = new BigDecimal(ZERO);
        if (G_NO_ITEM.equals(groupBelong)) {
            if (StringUtils.isNotEmpty(item.getItemValue())) {
                String[] itemValues = item.getItemValue().split(COMMA);
                for (String itemId : itemValues) {
                    if (salaryAcctResultValueMap.get(sa.getId()) != null && StringUtils.isNotBlank(salaryAcctResultValueMap.get(sa.getId()).get(itemId))) {
                        sumDecimal = sumDecimal.add(new BigDecimal(salaryAcctResultValueMap.get(sa.getId()).get(itemId)));
                    }
                }
            }
        } else if (G_ITEM.equals(groupBelong)) {
            if (salaryAcctResultValueMap.get(sa.getId()) != null && StringUtils.isNotBlank(salaryAcctResultValueMap.get(sa.getId()).get(dimCode))) {
                sumDecimal = sumDecimal.add(new BigDecimal(salaryAcctResultValueMap.get(sa.getId()).get(dimCode)));
            }
        } else if (G_YEAR.equals(groupBelong)) {
            if (StringUtils.isNotBlank(yearMap.get(sa.getEmployeeId()))) {
                sumDecimal = new BigDecimal(yearMap.get(sa.getEmployeeId()));
            }
        }
        return sumDecimal;
    }

    private static String handle4GroupValue(String dimCode, SalaryAcctEmployeePO sa, Map<Long, Map<String, String>> salaryAcctResultValueMap, SalaryStatisticsItemPO item) {
        if (salaryAcctResultValueMap.get(sa.getId()) != null) {
            String value = salaryAcctResultValueMap.get(sa.getId()).get(dimCode);
            if (StringUtils.isNotBlank(value)) {
                return value;
            }
        }
        return "";
    }

    /**
     * 定量-单项式分组list分割
     *
     * @param groupIndividual
     * @param groupBelong
     * @param dimCode
     * @param list
     * @param yearMap
     * @param salaryAcctResultValueMap
     * @param salaryStatisticsItemList
     * @return
     */
    public static List<SalaryAcctEmployeePO> listAcctEmpByRationGroupIndividual(SalaryStatisticsDimensionSaveParam.Setting4RationGroupIndividual groupIndividual, String groupBelong, String dimCode, List<SalaryAcctEmployeePO> list, Map<Long, String> yearMap, Map<Long, Map<String, String>> salaryAcctResultValueMap, List<SalaryStatisticsItemPO> salaryStatisticsItemList) {
        Set<Long> ids = Sets.newHashSet();

        for (SalaryAcctEmployeePO sa : list) {
            salaryStatisticsItemList.forEach(item -> {
                BigDecimal sumDecimal = handle4GroupValue(groupBelong, dimCode, sa, yearMap, salaryAcctResultValueMap, item);

                if (sumDecimal.compareTo(new BigDecimal(groupIndividual.getValue())) == 0) {
                    ids.add(sa.getId());
                }
            });
        }

        return list.stream().filter(i -> ids.contains(i.getId())).collect(Collectors.toList());
    }

    public static List<SalaryAcctEmployeePO> listAcctEmpByRationGroupIndividual(String groupIndividual, String dimCode, List<SalaryAcctEmployeePO> list, Map<Long, String> yearMap, Map<Long, Map<String, String>> salaryAcctResultValueMap, List<SalaryStatisticsItemPO> salaryStatisticsItemList) {
        Set<Long> ids = Sets.newHashSet();

        for (SalaryAcctEmployeePO sa : list) {
            salaryStatisticsItemList.forEach(item -> {
                String value = handle4GroupValue(dimCode, sa, salaryAcctResultValueMap, item);

                if (StrUtil.equals(groupIndividual, value)) {
                    ids.add(sa.getId());
                }
            });
        }

        return list.stream().filter(i -> ids.contains(i.getId())).collect(Collectors.toList());
    }

    public static Map<String, Boolean> checkLoad(List<SalaryStatisticsItemPO> salaryStatisticsItemList) {
        boolean isNow = false;
        boolean isLast = false;
        boolean isSame = false;
        for (SalaryStatisticsItemPO item : salaryStatisticsItemList) {
            SalaryStatisticsItemRuleDTO countRule = JSON.parseObject(item.getCountRule(), SalaryStatisticsItemRuleDTO.class);
            SalaryStatisticsItemRuleDTO sumRule = JSON.parseObject(item.getSumRule(), SalaryStatisticsItemRuleDTO.class);
            SalaryStatisticsItemRuleDTO avgRule = JSON.parseObject(item.getAvgRule(), SalaryStatisticsItemRuleDTO.class);
            SalaryStatisticsItemRuleDTO maxRule = JSON.parseObject(item.getMaxRule(), SalaryStatisticsItemRuleDTO.class);
            SalaryStatisticsItemRuleDTO minRule = JSON.parseObject(item.getMinRule(), SalaryStatisticsItemRuleDTO.class);
            SalaryStatisticsItemRuleDTO medianRule = JSON.parseObject(item.getMedianRule(), SalaryStatisticsItemRuleDTO.class);

            if ((countRule != null && countRule.getTotalValue() == 1) || (sumRule != null && sumRule.getTotalValue() == 1) || (avgRule != null && avgRule.getTotalValue() == 1) || (maxRule != null && maxRule.getTotalValue() == 1) || (minRule != null && minRule.getTotalValue() == 1) || (medianRule != null && medianRule.getTotalValue() == 1)) {
                isNow = true;
            }
            if ((countRule != null && countRule.getM2mValue() == 1) || (sumRule != null && sumRule.getM2mValue() == 1) || (avgRule != null && avgRule.getM2mValue() == 1) || (maxRule != null && maxRule.getM2mValue() == 1) || (minRule != null && minRule.getM2mValue() == 1) || (medianRule != null && medianRule.getM2mValue() == 1)) {
                isLast = true;
            }
            if ((countRule != null && countRule.getY2yValue() == 1) || (sumRule != null && sumRule.getY2yValue() == 1) || (avgRule != null && avgRule.getY2yValue() == 1) || (maxRule != null && maxRule.getY2yValue() == 1) || (minRule != null && minRule.getY2yValue() == 1) || (medianRule != null && medianRule.getY2yValue() == 1)) {
                isSame = true;
            }
        }
        Map<String, Boolean> map = Maps.newHashMap();
        map.put("isNow", isNow);
        map.put("isLast", isLast);
        map.put("isSame", isSame);
        return map;
    }

    /**
     * 构建合计
     *
     * @param page
     * @param salaryStatisticsItemList
     * @param employeeId
     */
    public static Map<String, Object> buildTotal(PageInfo<Map<String, Object>> page, List<SalaryStatisticsItemPO> salaryStatisticsItemList, Long employeeId) {
        Map<String, SalaryStatisticsItemPO> salaryStatisticsItemMap = SalaryEntityUtil.convert2Map(salaryStatisticsItemList, k -> k.getId() + "-" + employeeId);

        List<Map<String, Object>> records = page.getList();
        if (CollectionUtils.isNotEmpty(records)) {
            Set<String> keys = records.get(0).keySet();
            Map<String, Object> map = new HashMap<>();
            map.put("dimension", SalaryI18nUtil.getI18nLabel(93278, "合计"));
            Map<Long, SalaryStatisticsItemPO> itemPOMap = new HashMap<>();
            keys.forEach(key -> {
                if (key.contains(COUNT + K_NOW) || key.contains(COUNT + K_LAST) || key.contains(COUNT + K_SAME)
                        || key.contains(SUM + K_NOW) || key.contains(SUM + K_LAST) || key.contains(SUM + K_SAME)
                        || key.contains(AVG + K_NOW) || key.contains(AVG + K_LAST) || key.contains(AVG + K_SAME)
                        || key.contains(MAX + K_NOW) || key.contains(MAX + K_LAST) || key.contains(MAX + K_SAME)
                        || key.contains(MIN + K_NOW) || key.contains(MIN + K_LAST) || key.contains(MIN + K_SAME)
                        || key.contains(MEDIAN + K_NOW) || key.contains(MEDIAN + K_LAST) || key.contains(MEDIAN + K_SAME)) {
                    String keyStr = key.split(UD)[0];
                    SalaryStatisticsItemPO salaryStatisticsItem = salaryStatisticsItemMap.get(Long.valueOf(keyStr) + "-" + employeeId);
                    itemPOMap.put(salaryStatisticsItem.getId(), salaryStatisticsItem);
                    // 累加
                    BigDecimal countNow = new BigDecimal(ZERO);
                    for (Map<String, Object> kv : records) {
                        String temp = String.valueOf(kv.get(key)).replace(COMMA, "");
                        countNow = countNow.add(new BigDecimal(temp));
                    }
                    map.put(key, ReportDataUtil.thousandthConvert(unitTypeConvert(countNow, UnitTypeEnum.YUAN.getValue())));
                }
                // 有占比的话
                if (key.contains(K_RATIO)) {
                    map.put(key, "100%");
                }
            });
            Set<String> keySet = new HashSet<>(map.keySet());
            List<String> ruleKeys = Arrays.asList(COUNT, SUM, AVG, MAX, MIN, MEDIAN);
            String keyPrefix;
            for (String key : keySet) {
                if (key.equals("dimension") || key.contains(K_RATIO)) {
                    continue;
                }
                for (String rule : ruleKeys) {
                    String idStr = key.split(UD)[0];
                    keyPrefix = idStr + UD + rule;
                    SalaryStatisticsItemPO salaryStatisticsItem = itemPOMap.get(Long.valueOf(idStr));
                    String countNowValue = Optional.ofNullable(map.get(keyPrefix + K_NOW)).orElse(D_ZERO).toString().replace(COMMA, "");
                    BigDecimal countNow = new BigDecimal(countNowValue);
                    String countLastValue = Optional.ofNullable(map.get(keyPrefix + K_LAST)).orElse(D_ZERO).toString().replace(COMMA, "");
                    BigDecimal countLast = new BigDecimal(countLastValue);
                    map.put(keyPrefix + P_M2M_INCREASE, radio(countNow, countLast));
                    map.put(keyPrefix + P_M2M_D_VALUE, ReportDataUtil.thousandthConvert(unitTypeConvert(countNow.subtract(countLast), UnitTypeEnum.YUAN.getValue())));
                    map.put(keyPrefix + P_M2M_LOWER_LIMIT, "");
                    map.put(keyPrefix + P_M2M_UPPER_LIMIT, "");
                    String countSameValue = Optional.ofNullable(map.get(keyPrefix + K_SAME)).orElse(D_ZERO).toString().replace(COMMA, "");
                    BigDecimal countSame = new BigDecimal(countSameValue);
                    map.put(keyPrefix + P_Y2Y_INCREASE, radio(countNow, countSame));
                    map.put(keyPrefix + P_Y2Y_D_VALUE, ReportDataUtil.thousandthConvert(unitTypeConvert(countNow.subtract(countSame), UnitTypeEnum.YUAN.getValue())));
                    map.put(keyPrefix + P_Y2Y_LOWER_LIMIT, "");
                    map.put(keyPrefix + P_Y2Y_UPPER_LIMIT, "");
                }
            }
            // 占比计算
            for (Map<String, Object> record : records) {
                keys.forEach(key -> {
                    if (key.contains(K_RATIO)) {
                        String total = String.valueOf(map.get(key.replace(K_RATIO, K_NOW))).replace(COMMA, "");
                        String temp = String.valueOf(record.get(key)).replace(COMMA, "");
                        record.put(key, total.equals(D_ZERO) ? D_ZERO + PERCENT : (new BigDecimal(temp).multiply(HUNDRED).divide(new BigDecimal(total), 2, RoundingMode.HALF_UP)).toPlainString() + PERCENT);
                    }
                });
            }
            return map;
//            records.add(map);
        }
        return Maps.newHashMap();
    }

    /**
     * 构建报表动态列
     *
     * @param dimensionName
     * @param salaryStatisticsItemList
     * @return
     */
    public static List<WeaTableColumnGroup> buildReportColumns(SalaryStatisticsDimensionPO dimensionName, List<SalaryStatisticsItemPO> salaryStatisticsItemList) {
        List<WeaTableColumnGroup> result = new ArrayList<>();

        result.add(new WeaTableColumnGroup("150", dimensionName.getDimName(), "dimension"));

        //扩展维度属性
        EmployeeInfoExpandDTO expandFieldSettings = getSalaryStatisticsDimensionService().getExpandFieldSettings("dim_" + dimensionName.getDimCode());
        List<FieldSetting> fieldSettings = Optional.ofNullable(Optional.ofNullable(expandFieldSettings).orElse(new EmployeeInfoExpandDTO()).getFieldSettings()).orElse(new ArrayList<>());
        fieldSettings.stream().sorted(Comparator.comparing(FieldSetting::getIndex)).forEach(setting -> {
            result.add(new WeaTableColumnGroup("150", setting.getName(), setting.getField()));
        });

        Map<Long, SalaryStatisticsItemPO> itemMap = salaryStatisticsItemList.stream().collect(Collectors.toMap(SalaryStatisticsItemPO::getId, Function.identity()));
        Map<Long, String> itemIdNameMap = salaryStatisticsItemList.stream().collect(LinkedHashMap::new, (map, item) -> map.put(item.getId(), item.getItemName()), LinkedHashMap::putAll);
        itemIdNameMap.forEach((k, v) -> {
            WeaTableColumnGroup weaTableColumn = new WeaTableColumnGroup("300", v, k.toString());
            SalaryStatisticsItemPO itemPO = itemMap.get(k);
            List<WeaTableColumnGroup> children = new ArrayList<>();
            // 1.合计规则children
            SalaryStatisticsItemRuleDTO countRule = JSON.parseObject(itemPO.getCountRule(), SalaryStatisticsItemRuleDTO.class);
            if (countRule != null && 1 == countRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157127, "本周期(计数)"), k + UD + COUNT + P_NOW));
            }
            if (countRule != null && 1 == countRule.getRatioValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174288, "本周期占比(计数)"), k + UD + COUNT + P_RATIO));
            }
            if (countRule != null && 1 == countRule.getM2mValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157128, "上周期(计数)"), k + UD + COUNT + P_LAST));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157129, "计数环比差值"), k + UD + COUNT + P_M2M_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157130, "计数环比增幅"), k + UD + COUNT + P_M2M_INCREASE));
            }
            if (countRule != null && 1 == countRule.getY2yValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(180672, "同比周期(计数)"), k + UD + COUNT + P_SAME));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174391, "计数同比差值"), k + UD + COUNT + P_Y2Y_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174392, "计数同比增幅"), k + UD + COUNT + P_Y2Y_INCREASE));
            }
            // 2.求和规则children
            SalaryStatisticsItemRuleDTO totalRule = JSON.parseObject(itemPO.getSumRule(), SalaryStatisticsItemRuleDTO.class);
            if (totalRule != null && 1 == totalRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157145, "本周期(求和)"), k + UD + SUM + P_NOW));
            }
            if (totalRule != null && 1 == totalRule.getRatioValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174289, "本周期占比(求和)"), k + UD + SUM + P_RATIO));
            }
            if (totalRule != null && 1 == totalRule.getM2mValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157146, "上周期(求和)"), k + UD + SUM + P_LAST));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157147, "求和环比差值"), k + UD + SUM + P_M2M_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(157148, "求和环比增幅"), k + UD + SUM + P_M2M_INCREASE));
            }
            if (totalRule != null && 1 == totalRule.getY2yValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(180673, "同比周期(求和)"), k + UD + SUM + P_SAME));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174388, "求和同比差值"), k + UD + SUM + P_Y2Y_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174389, "求和同比增幅"), k + UD + SUM + P_Y2Y_INCREASE));
            }
            // 3.平均值规则children
            SalaryStatisticsItemRuleDTO avgRule = JSON.parseObject(itemPO.getAvgRule(), SalaryStatisticsItemRuleDTO.class);
            if (avgRule != null && 1 == avgRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174258, "本周期(平均值)"), k + UD + AVG + P_NOW));
            }
            if (avgRule != null && 1 == avgRule.getRatioValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174290, "本周期占比(平均值)"), k + UD + AVG + P_RATIO));
            }
            if (avgRule != null && 1 == avgRule.getM2mValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174257, "上周期(平均值)"), k + UD + AVG + P_LAST));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174256, "平均值环比差值"), k + UD + AVG + P_M2M_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174255, "平均值环比增幅"), k + UD + AVG + P_M2M_INCREASE));
            }
            if (avgRule != null && 1 == avgRule.getY2yValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(180674, "同比周期(平均值)"), k + UD + AVG + P_SAME));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174385, "平均值同比差值"), k + UD + AVG + P_Y2Y_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174386, "平均值同比增幅"), k + UD + AVG + P_Y2Y_INCREASE));
            }
            // 4.最大值规则children
            SalaryStatisticsItemRuleDTO maxRule = JSON.parseObject(itemPO.getMaxRule(), SalaryStatisticsItemRuleDTO.class);
            if (maxRule != null && 1 == maxRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174254, "本周期(最大值)"), k + UD + MAX + P_NOW));
            }
            if (maxRule != null && 1 == maxRule.getRatioValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174291, "本周期占比(最大值)"), k + UD + MAX + P_RATIO));
            }
            if (maxRule != null && 1 == maxRule.getM2mValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174253, "上周期(最大值)"), k + UD + MAX + P_LAST));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174252, "最大值环比差值"), k + UD + MAX + P_M2M_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174251, "最大值环比增幅"), k + UD + MAX + P_M2M_INCREASE));
            }
            if (maxRule != null && 1 == maxRule.getY2yValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(180675, "同比周期(最大值)"), k + UD + MAX + P_SAME));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174382, "最大值同比差值"), k + UD + MAX + P_Y2Y_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174383, "最大值同比增幅"), k + UD + MAX + P_Y2Y_INCREASE));
            }
            // 5.最小值规则children
            SalaryStatisticsItemRuleDTO minRule = JSON.parseObject(itemPO.getMinRule(), SalaryStatisticsItemRuleDTO.class);
            if (minRule != null && 1 == minRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174250, "本周期(最小值)"), k + UD + MIN + P_NOW));
            }
            if (minRule != null && 1 == minRule.getRatioValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174292, "本周期占比(最小值)"), k + UD + MIN + P_RATIO));
            }
            if (minRule != null && 1 == minRule.getM2mValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174249, "上周期(最小值)"), k + UD + MIN + P_LAST));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174248, "最小值环比差值"), k + UD + MIN + P_M2M_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174247, "最小值环比增幅"), k + UD + MIN + P_M2M_INCREASE));
            }
            if (minRule != null && 1 == minRule.getY2yValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(180676, "同比周期(最小值)"), k + UD + MIN + P_SAME));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174379, "最小值同比差值"), k + UD + MIN + P_Y2Y_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174380, "最小值同比增幅"), k + UD + MIN + P_Y2Y_INCREASE));
            }
            // 6.中位数规则children
            SalaryStatisticsItemRuleDTO medianRule = JSON.parseObject(itemPO.getMedianRule(), SalaryStatisticsItemRuleDTO.class);
            if (medianRule != null && 1 == medianRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174246, "本周期(中位数)"), k + UD + MEDIAN + P_NOW));
            }
            if (medianRule != null && 1 == medianRule.getRatioValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174293, "本周期占比(中位数)"), k + UD + MEDIAN + P_RATIO));
            }
            if (medianRule != null && 1 == medianRule.getM2mValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174245, "上周期(中位数)"), k + UD + MEDIAN + P_LAST));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174244, "中位数环比差值"), k + UD + MEDIAN + P_M2M_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174243, "中位数环比增幅"), k + UD + MEDIAN + P_M2M_INCREASE));
            }
            if (medianRule != null && 1 == medianRule.getY2yValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(180677, "同比周期(中位数)"), k + UD + MEDIAN + P_SAME));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174376, "中位数同比差值"), k + UD + MEDIAN + P_Y2Y_D_VALUE));
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174377, "中位数同比增幅"), k + UD + MEDIAN + P_Y2Y_INCREASE));
            }
            // 7.最新值children
            SalaryStatisticsItemRuleDTO lastRule = JSON.parseObject(itemPO.getLastRule(), SalaryStatisticsItemRuleDTO.class);
            if (lastRule != null && 1 == lastRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174246, "最新值"), k + UD + SalaryStatisticsItemStringRuleEnum.LAST.getValue() + P_NOW));
            }
            // 8.最旧值children
            SalaryStatisticsItemRuleDTO oldRule = JSON.parseObject(itemPO.getOldRule(), SalaryStatisticsItemRuleDTO.class);
            if (oldRule != null && 1 == oldRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174246, "最旧值"), k + UD + SalaryStatisticsItemStringRuleEnum.OLD.getValue() + P_NOW));
            }
            // 9.最频繁children
            SalaryStatisticsItemRuleDTO frequentRule = JSON.parseObject(itemPO.getFrequentRule(), SalaryStatisticsItemRuleDTO.class);
            if (frequentRule != null && 1 == frequentRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174246, "最频繁"), k + UD + SalaryStatisticsItemStringRuleEnum.FREQUENT.getValue() + P_NOW));
            }
            // 10.平铺children
            SalaryStatisticsItemRuleDTO tileRule = JSON.parseObject(itemPO.getTileRule(), SalaryStatisticsItemRuleDTO.class);
            if (tileRule != null && 1 == tileRule.getTotalValue()) {
                children.add(new WeaTableColumnGroup(COL_WIDTH, SalaryI18nUtil.getI18nLabel(174246, "平铺"), k + UD + SalaryStatisticsItemStringRuleEnum.TILE.getValue() + P_NOW));
            }
            weaTableColumn.setChildren(children);
            result.add(weaTableColumn);
        });
        return result;
    }

    public static ExportCommonParam buildExportParam(String dimName, List<WeaTableColumn> weaTableColumns, List<Map<String, String>> records) {
        List<ExportColumn> columns = com.google.common.collect.Lists.newArrayList();
        weaTableColumns.forEach(column -> {
            WeaTableColumnGroup WeaTableColumnGroup = (WeaTableColumnGroup) column;
            ExportColumn exportColumn = new ExportColumn();
            exportColumn.setKeyIndex(WeaTableColumnGroup.getColumn());
            exportColumn.setValue(WeaTableColumnGroup.getText());
            if (CollectionUtils.isNotEmpty(WeaTableColumnGroup.getChildren())) {
                List<ExportColumn> subColumns = com.google.common.collect.Lists.newArrayList();
                for (WeaTableColumnGroup sub : WeaTableColumnGroup.getChildren()) {
                    ExportColumn subColumn = new ExportColumn();
                    subColumn.setKeyIndex(sub.getColumn());
                    subColumn.setValue(sub.getText());
                    subColumns.add(subColumn);
                }
                exportColumn.setChildren(subColumns);
            }
            columns.add(exportColumn);
        });
        ExportCommonParam exportParam = new ExportCommonParam();
        exportParam.setDimensionName(dimName);
        exportParam.setColumns(columns);
        exportParam.setData(records);

        return exportParam;
    }
}
